# -*- coding: utf-8 -*-
import pandas as pd
import mplfinance as mpf
import matplotlib.pyplot as plt
from binance.client import Client
from datetime import datetime, timedelta
from matplotlib.widgets import SpanSelector, Slider

# 1. 设置全局中文字体
plt.rcParams['font.family'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False


# 2. 获取Binance数据
def get_binance_data(symbol='BTCUSDT', interval='1d', days=180):
    proxies = {
        'http': 'socks5://127.0.0.1:10010',
        'https': 'socks5://127.0.0.1:10010',
    }
    # 初始化Binance客户端 (不需要API密钥获取K线数据)
    client = Client(requests_params={'proxies': proxies, 'timeout': 10})
    end_date = datetime.now()
    start_date = end_date - timedelta(days=days)

    klines = client.get_historical_klines(
        symbol=symbol,
        interval=interval,
        start_str=start_date.strftime('%Y-%m-%d'),
        end_str=end_date.strftime('%Y-%m-%d')
    )

    df = pd.DataFrame(klines, columns=[
        'Open time', 'Open', 'High', 'Low', 'Close', 'Volume',
        'Close time', 'Quote asset volume', 'Number of trades',
        'Taker buy base asset volume', 'Taker buy quote asset volume', 'Ignore'
    ])

    df['Open time'] = pd.to_datetime(df['Open time'], unit='ms')
    df.set_index('Open time', inplace=True)
    return df[['Open', 'High', 'Low', 'Close', 'Volume']].astype(float)


# 3. 获取数据
data = get_binance_data(symbol='BTCUSDT', interval='1d', days=180)

# 4. 创建图表布局
fig = plt.figure(figsize=(12, 10))
gs = fig.add_gridspec(3, 1, height_ratios=[6, 1, 1], hspace=0.1)
ax_main = fig.add_subplot(gs[0])  # 主K线图
ax_thumb = fig.add_subplot(gs[1])  # 缩略图
ax_slider = fig.add_subplot(gs[2])  # 缩放控制条

# 5. 绘制主K线图和缩略图
mpf.plot(data, type='candle', style='charles', ax=ax_main, volume=False)
mpf.plot(data, type='candle', style='charles', ax=ax_thumb, volume=False)

# 缩小缩略图的字体和线宽
for item in ax_thumb.get_xticklabels() + ax_thumb.get_yticklabels():
    item.set_fontsize(8)
ax_thumb.tick_params(axis='both', which='major', labelsize=8)
for line in ax_thumb.lines:
    line.set_linewidth(0.5)

# 6. 添加缩放控制条
zoom_slider = Slider(
    ax=ax_slider,
    label='缩放控制',
    valmin=7,  # 最小显示7天
    valmax=len(data),
    valinit=30,  # 默认显示30天
    valstep=1,
    valfmt='%1.0f 天'
)


# 7. 缩放控制回调函数
def update_zoom(val):
    visible_days = int(val)
    end_idx = len(data) - 1
    start_idx = max(0, end_idx - visible_days + 1)
    ax_main.set_xlim(start_idx, end_idx)
    fig.canvas.draw_idle()


zoom_slider.on_changed(update_zoom)


# 8. 添加区域选择缩放
def onselect(xmin, xmax):
    ax_main.set_xlim(xmin, xmax)
    # 更新滑块位置
    visible_days = xmax - xmin
    zoom_slider.set_val(visible_days)
    fig.canvas.draw_idle()


span = SpanSelector(
    ax_thumb,
    onselect,
    'horizontal',
    useblit=True,
    props=dict(alpha=0.5, facecolor='blue')
)


# 9. 添加交互功能
class CandleHover:
    def __init__(self):
        self.annotation = None
        self.vline = None

    def __call__(self, event):
        if not event.inaxes or event.xdata is None or event.inaxes in [ax_thumb, ax_slider]:
            self._cleanup()
            return

        try:
            idx = int(round(event.xdata))
            if 0 <= idx < len(data):
                candle = data.iloc[idx]
                self._update_annotations(idx, candle, event)
            else:
                self._cleanup()
        except:
            self._cleanup()

        fig.canvas.draw_idle()

    def _update_annotations(self, idx, candle, event):
        change = (candle['Close'] - candle['Open']) / candle['Open'] * 100
        self._cleanup()

        self.vline = ax_main.axvline(x=idx, color='dodgerblue', alpha=0.7, linestyle='--')

        text = (f"日期: {data.index[idx].strftime('%Y-%m-%d')}\n"
                f"开盘: {candle['Open']:.2f}\n"
                f"收盘: {candle['Close']:.2f}\n"
                f"涨跌幅: {change:+.2f}%")

        self.annotation = ax_main.annotate(
            text,
            xy=(idx, candle['High']),
            xytext=(10, 10),
            textcoords='offset points',
            bbox=dict(boxstyle='round', fc='w', ec='0.5', alpha=0.9),
            arrowprops=dict(arrowstyle='->', connectionstyle='arc3')
        )

    def _cleanup(self):
        if self.annotation:
            self.annotation.remove()
            self.annotation = None
        if self.vline:
            self.vline.remove()
            self.vline = None


# 10. 绑定事件
hover = CandleHover()
fig.canvas.mpl_connect('motion_notify_event', hover)

# 11. 添加重置按钮
ax_reset = plt.axes([0.8, 0.01, 0.15, 0.04])
btn_reset = plt.Button(ax_reset, '重置视图', color='lightgreen')


def reset_view(event):
    ax_main.set_xlim(0, len(data) - 1)
    zoom_slider.set_val(30)  # 重置为默认30天
    fig.canvas.draw_idle()


btn_reset.on_clicked(reset_view)

plt.tight_layout()
plt.show()